
#include <BFile>
#include <BOpenGL>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BStyleDocument>
#include <BGroupItem>

#include "IGlobal.h"
#include "member_BGroupItem.h"
#include "member_BGraphicWidget.h"
#include "member_BStyle.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BGroupItem*)_ptr)
#define member_allocate()		_ptr = new member_BGroupItem(this)
#define member_release()		delete (member_BGroupItem*)_ptr

BGroupItem::BGroupItem(BGraphicItem* parent) : BGraphicItem(parent)
{
	member_allocate();
}
BGroupItem::BGroupItem(const BString& name, BGraphicItem* parent) : BGraphicItem(name, parent)
{
	member_allocate();
}
BGroupItem::BGroupItem(BWidget* widget, BGraphicItem* parent) : BGraphicItem(widget, parent)
{
	member_allocate();
}
BGroupItem::BGroupItem(BIcon* icon, const BString& title, BGraphicItem* parent) : BGraphicItem(parent)
{
	member_allocate();
	setIcon(icon);
	setTitle(title);
}
BGroupItem::~BGroupItem()
{
	member_release();
}

bool BGroupItem::setTitleAlign(Align titleAlign)
{
	if (member.ia_align != titleAlign)
	{
		member.ia_align = titleAlign;
		member.dirty = true;
		this->emit(Signal_Changed);
		this->fresh();
		return true;
	}
	return false;
}
Align BGroupItem::titleAlign() const
{
	return member.ia_align;
}

bool BGroupItem::setTitleForm(Form titleForm)
{
	if (member.ia_form != titleForm)
	{
		member.ia_form = titleForm;
		member.dirty = true;
		this->emit(Signal_FormChanged, titleForm);
		this->fresh();
		return true;
	}
	return false;
}
Form BGroupItem::titleForm() const
{
	return member.ia_form;
}

bool BGroupItem::setIconSize(int iconWidth, int iconHeight)
{
	return setIconSize(BSize(iconWidth, iconHeight));
}
bool BGroupItem::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BSize& BGroupItem::iconSize() const
{
	return member.iconSize;
}

bool BGroupItem::setTitle(const BString& title)
{
	if (member.title != title)
	{
		member.title = title;
		member.dirty = true;
		this->emit(Signal_TitleChanged, member.title);
		this->fresh();
		return true;
	}
	return false;
}
const BString& BGroupItem::title() const
{
	return member.title;
}

bool BGroupItem::setIcon(BIcon* icon)
{
	if (member.icon != icon)
	{
		member.icon = icon;
		member.dirty = true;
		this->emit(Signal_IconChanged, member.icon);
		this->fresh();
		return true;
	}
	return false;
}
const BIcon* BGroupItem::icon() const
{
	return member.icon;
}
BIcon* BGroupItem::icon()
{
	return member.icon;
}

void BGroupItem::setFlat(bool flat)
{
	if (member.flat != flat)
	{
		member.flat = flat;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupItem::flat() const
{
	return member.flat;
}

void BGroupItem::setWired(bool wired)
{
	if (member.wired != wired)
	{
		member.wired = wired;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupItem::wired() const
{
	return member.wired;
}

void BGroupItem::setFolded(bool folded)
{
	if (member.folded != folded)
	{
		member.folded = folded;
		if (folded)
		{
			if (const BWidget* widget = this->widget())
			{
				member.centerSize = widget->sizeHint();
			}
			member.backupPerch = this->perch();
			this->setPerch(Part_Bottom, 0);
			member.backupMargin = this->margin();
			this->setMargin(Part_Top | Part_Bottom, 0);
		}
		else
		{
			this->setPerch(member.backupPerch);
			this->setMargin(member.backupMargin);
		}
		member.dirty = true;
		this->emit(Signal_Folded, member.folded);
		this->fresh();
	}
}
bool BGroupItem::folded() const
{
	return member.folded;
}

void BGroupItem::setCheckable(bool checkable)
{
	if (member.checkable != checkable)
	{
		member.checkable = checkable;
		member.checked = false;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupItem::checkable() const
{
	return member.checkable;
}

void BGroupItem::setChecked(bool checked)
{
	if (member.checkable)
	{
		if (member.checked != checked)
		{
			member.checked = checked;
			emit(Signal_Checked, checked);
			this->fresh();
		}
	}
}
bool BGroupItem::checked() const
{
	return member.checked;
}

BSizef BGroupItem::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects(this->realStyle());
		member.dirty = false;
	}
	float hintWidth = member.ia_hintSize.width();
	if (member.folded)
	{
		if (hintWidth < member.centerSize.width())
			hintWidth = member.centerSize.width();
	}
	return this->preferSize(hintWidth, 0);
}

bool BGroupItem::query(const BString& text, BValue& value) const
{
	return BObject::query(text, value);
}

void BGroupItem::paint(BPainter& painter)
{
	drawBackground(painter);
	drawForeground(painter);
	drawPerch(painter);
	drawTitle(painter);

	if (member.wired && painter.setColor(Color_Wire))
	{
		painter.setLineWidth((float)member.wireSize);
		if (member.flat || member.folded)
		{
			painter.drawLine(member.wirePoints.first(), member.wirePoints(1));
			painter.drawLine(member.wirePoints.last(), member.wirePoints(member.wirePoints.size() - 2));
		}
		else
		{
			painter.setLineSmooth(true);
			painter.drawLineStrip(member.wirePoints);
			painter.setLineSmooth(false);
		}
	}
	State state = painter.state() | member.state;
	if (member.titleVisible)
	{
		if (member.checkable && member.checked)
		{
			if (painter.setColor(Color_Perch, state))
			{
				painter.fillRect(member.titleArea);
			}
		}
		if (member.ia_iconRect.valid())
		{
			BRectf iconRect = member.ia_iconRect;
			if (member.iconSize.empty())
				iconRect = this->rect(Part_Top).wrap(iconRect);
			if (BIcon* icon = this->icon())
			{
				if (BImage* image = icon->image(state))
				{
					painter.drawImage(iconRect, image);
				}
			}
			else if (member.checkable)
			{
				painter.setColor(Color_Arrow, state);
				painter.setLineSmooth(true);
				painter.setLineWidth(2);
				painter.drawRect(iconRect % 86);
				if (member.checked)
				{
					iconRect %= 66;
					BPointf p0 = iconRect.leftCenter();
					BPointf p1 = iconRect.bottomCenter();
					BPointf p2 = iconRect.rightTop();
					painter.drawLine(p0, p1);
					painter.drawLine(p1, p2);
				}
			}
		}
		if (painter.setColor(Color_Text, state))
		{
			if (member.t_title.size())
				painter.drawText(member.ia_textRect, member.t_title);
			else
				painter.drawText(member.ia_textRect, this->title());
		}
	}

	drawMargin(painter);
	drawBorder(painter);
}

void BGroupItem::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BGraphicItem::resizeEvent(event);
}

void BGroupItem::styleEvent(const BEvent& event)
{
	BGraphicItem::styleEvent(event);

	const BStyle* style = event.value();

	member.autoTop = style->value(Value_Perch_Top).empty();
	member.autoLeft = style->value(Value_Perch_Left).empty();
	member.autoRight = style->value(Value_Perch_Right).empty();
	member.autoBottom = style->value(Value_Perch_Bottom).empty();

	const BValue& alignValue = style->value(Value_Align);
	if (alignValue.valid())
		member.ia_align = alignValue;

	const BValue& formValue = style->value(Value_Form);
	if (formValue.valid())
		member.ia_form = formValue;

	const BValue& titleValue = style->value(Value_Title);
	if (titleValue.valid())
		member.title = (const BString&)titleValue;

	const BValue& iconValue = style->value(Value_Icon);
	if (iconValue.valid())
		member.icon = (BIcon*)iconValue;

	const BValue& iconSizeValue = style->value(Value_Icon_Size);
	if (iconSizeValue.valid())
		member.iconSize = iconSizeValue;

}
void BGroupItem::freshEvent(const BEvent& event)
{
	BGraphicItem::freshEvent(event);
	if (member.dirty)
	{
		const BStyle* style = this->realStyle();
		const BFont* font = style->font();
		BPerchf perch = this->perch();
		if (member.autoTop && member.titleVisible)
		{
			if (perch.top() < font->size() && font)
				perch.top() = (float)font->size();
			if (perch.top() < member.iconSize.height())
				perch.top() = (float)member.iconSize.height();
		}
		if (!member.flat)
		{
			if (member.autoLeft)
				perch.left() = perch.top() / 2;
			if (member.autoRight)
				perch.right() = perch.top() / 2;
			if (member.autoBottom)
			{
				if (member.folded)
					perch.bottom() = 0;
				else
					perch.bottom() = perch.top() / 2;
			}
		}
		this->setPerch(perch);
		member.freshRects(style);
		member.dirty = false;
	}
}

void BGroupItem::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Normal)
	{
		member.state = State_Normal;
		this->fresh();
	}
	return BGraphicItem::mouseEnterEvent(mouseEvent);
}
void BGroupItem::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Normal)
	{
		member.state = State_Normal;
		this->fresh();
	}
	return BGraphicItem::mouseLeaveEvent(mouseEvent);
}

void BGroupItem::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (member.titleArea.contain(pos))
	{
		member.state = State_Pressed;
		this->fresh();
		mouseEvent.accept();
	}
	BGraphicItem::mousePressEvent(mouseEvent);
}
void BGroupItem::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.state == State_Pressed)
	{
		member.state = member.titleArea.contain(pos) ? State_Hovered : State_Normal;
		this->fresh();
		mouseEvent.accept();
	}
	BGraphicItem::mouseReleaseEvent(mouseEvent);
}
void BGroupItem::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_None)
	{
		const BPoint& pos = mouseEvent.pos();
		State state = member.titleArea.contain(pos) ? State_Hovered : State_Normal;
		if (member.state != state)
		{
			member.state = state;
			this->fresh();
			mouseEvent.accept();
		}
	}
	BGraphicItem::mouseMoveEvent(mouseEvent);
}

void BGroupItem::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		if (member.checkable && member.titleArea.contain(pos))
		{
			setChecked(!member.checked);
			emit(Signal_Toggled, member.checked);
		}
	}
	BGraphicItem::mouseClickEvent(mouseEvent);
}
